/*-----------------------------------------------------------------------------
		   Licensed to the Apache Software Foundation (ASF) under one
           or more contributor license agreements.  See the NOTICE file
           distributed with this work for additional information
           regarding copyright ownership.  The ASF licenses this file
           to you under the Apache License, Version 2.0 (the
           "License"); you may not use this file except in compliance
           with the License.  You may obtain a copy of the License at

             http://www.apache.org/licenses/LICENSE-2.0

           Unless required by applicable law or agreed to in writing,
           software distributed under the License is distributed on an
           "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
           KIND, either express or implied.  See the License for the
           specific language governing permissions and limitations
           under the License.  
-----------------------------------------------------------------------------*/

package data;

import java.io.File;
import java.util.ArrayList;

/**
 * This class represents a source folder which is composed of many fields, and
 * contains the files found within the folder. There are metrics that gathered
 * based on the child folders and files within these folders.
 * <p>They are the base of the hierarchy structure {@link SFile} > 
 * {@link SFolder} > {@link SMethod}
 * <p> SLOC = Source Lines of Code <p> CC = Cyclomatic Complexity
 * <p> CR = Comment Ratio
 * 
 * @author Kevin.J.Jalbert
 */
public class SFolder {

	private String folderPath = null;
	private SFolder parentFolder = null;

	/*
	 * TODO Make use of these to save on performance; save info when a folder returns
	 * 		Check to see if there is a value, if so just use it, otherwise find it.
	 */
	private float totalSLOC = -2;
	private int totalFiles = -2;
	private int totalCC = -2;

	private ArrayList<SFile> allFiles = new ArrayList<SFile>();
	private ArrayList<SFolder> subFolders = new ArrayList<SFolder>();

	/**
	 * Instantiates a new generic {@link SFolder} using a file's path.
	 * 
	 * @param file the file
	 */
	public SFolder(SFile file) {
		this.folderPath = file.getFilePath();
	}

	/**
	 * Instantiates a new rootfolder {@link SFolder} which uses a file's path 
	 * by making use of the rootfolder specified.
	 * 
	 * @param file the file
	 * @param root used to represent that this is the rootfolder
	 */
	public SFolder(SFile file, boolean root) {
		this.folderPath = file.getFilePath() + file.getFileName() + File.separator;
	}

	/**
	 * Adds the a sub {@link SFolder} to the folder.
	 * 
	 * @param folder the subfolder to be added to the folder
	 */
	public void addSubSFolder(SFolder folder) {
		subFolders.add(folder);
	}

	/**
	 * Gets the arraylist of all {@link SFolder} in the folder.
	 * 
	 * @return allFiles an arraylist of all {@link SFolder} in the folder
	 */
	public ArrayList<SFolder> getSubSFolders() {
		return subFolders;
	}

	/**
	 * Adds a {@link SFile} to the folder.
	 * 
	 * @param file the file to be added to the folder
	 */
	public void addSFile(SFile file) {
		allFiles.add(file);
	}

	/**
	 * Gets the arraylist of all {@link SFile} in the folder.
	 * 
	 * @return allFiles an arraylist of all {@link SFile} in the folder
	 */
	public ArrayList<SFile> getSFiles() {
		return allFiles;
	}

	/**
	 * Sets the parent folder of current folder.
	 * 
	 * @param parentFolder the new parent folder
	 */
	public void setParentFolder(SFolder parentFolder) {
		this.parentFolder = parentFolder;
	}

	/**
	 * Gets the parent folder of current folder.
	 * 
	 * @return parentFolder the parent folder
	 */
	public SFolder getParentFolder() {
		return parentFolder;
	}

	/**
	 * Gets the folder's path.
	 * 
	 * @return folderPath the folder's path
	 */
	public String getFolderPath() {
		return folderPath;
	}

	/**
	 * Gets the folder size in bytes, recursively looks at all subfolders and files.
	 * 
	 * @return folderSize the folder size in bytes
	 */
	public float getFolderSize() {

		float folderSize = 0;

		// Recursively get the subfolder's size
		for (SFolder folder : subFolders) {
			folderSize += folder.getFolderSize();
		}

		// Get the all the file's size
		for (SFile file : allFiles) {
			folderSize += file.getFileSize();
		}

		return folderSize;
	}

	/**
	 * Gets the comment ratio, recursively looks at all subfolder and files.
	 * 
	 * @return commentRatio the comment ratio of the folder
	 */
	public float getCommentRatio() {

		float totalComments = 0;
		float totalUnComments = 0;
		float commentRatio = 0;

		// Get the subfolder's comment and uncomment characters
		for (SFolder folder : subFolders) {
			totalComments += folder.getCommentCharacters();
			totalUnComments += folder.getUnCommentCharacters();

		}

		// Get the all the file's comment and uncomment charcters
		for (SFile file : allFiles) {
			totalComments += file.getCharactersWithComments();
			totalUnComments += file.getCharactersWithoutComments();
		}

		if (totalComments == 0){
			commentRatio = -1;
		}
		else{
			commentRatio = (1 - (totalUnComments / totalComments)) * 100;
		}

		return commentRatio;
	}

	/**
	 * Gets the number of unCommentCharacters, using recursion.
	 * 
	 * @return totalUnComments the number of uncommented characters
	 */
	private float getUnCommentCharacters() {
		float totalUnComments = 0;

		// Recursively get the subfolder's uncomment characters
		for (SFolder folder : subFolders) {

			totalUnComments += folder.getUnCommentCharacters();
		}

		// Get the all the file's uncomment characters
		for (SFile file : allFiles) {
			totalUnComments += file.getCharactersWithoutComments();
		}

		return totalUnComments;
	}

	/**
	 * Gets the number of commentCharacters, using recursion.
	 * 
	 * @return totalComments the number of commented characters
	 */
	private float getCommentCharacters() {
		float totalComments = 0;

		// Recursively get the subfolder's comment characters
		for (SFolder folder : subFolders) {

			totalComments += folder.getCommentCharacters();
		}

		// Get the all the file's comment characters
		for (SFile file : allFiles) {
			totalComments += file.getCharactersWithComments();
		}

		return totalComments;
	}

	/**
	 * Gets the total CC, recursively looks at all subfolder and files.
	 * 
	 * @return totalCC the total CC of the folder
	 */
	public int getTotalCC() {

		int totalCC = -3;

		if (this.totalCC == -2) {

			totalCC = 0;

			// Recursively get the subfolder's total CC
			for (SFolder folder : subFolders) {
				totalCC += folder.getTotalCC();
			}

			// Get the all the file's total CC
			for (SFile file : allFiles) {
				totalCC += file.getCyclo();
			}

			if (totalCC == 0){
				totalCC = -1;
			}
		}
		return totalCC;
	}

	/**
	 * Gets the total SLOC, recursively looks at all subfolder and files.
	 * 
	 * @return totalSLOC the total SLOC of the folder
	 */
	public float getTotalSLOC() {

		float totalSLOC = -3;

		if (this.totalSLOC == -2) {

			totalSLOC = 0;

			// Recursively get the subfolder's total SLOC
			for (SFolder folder : subFolders) {
				totalSLOC += folder.getTotalSLOC();
			}

			// Get the all the file's total SLOC
			for (SFile file : allFiles) {
				totalSLOC += file.getSourceLines();
			}

			if (totalSLOC == 0){
				totalSLOC = -1;
			}

		}
		return totalSLOC;
	}

	/**
	 * Gets the total files, recursively looks at all subfolder and files.
	 * 
	 * @return totalFiles the total files in the folder
	 */
	public int getTotalFiles() {

		int totalFiles = -3;

		if (this.totalFiles == -2) {

			totalFiles = 0;

			// Recursively get the subfolder's total files
			for (SFolder folder : subFolders) {
				totalFiles += folder.getTotalFiles();
			}

			for (SFile file : allFiles) {
				totalFiles++;
			}

			if (totalFiles == 0){
				totalFiles = -1;
			}
		}

		return totalFiles;
	}

	/**
	 * Gets the SLOC per CC of the folder.
	 * 
	 * @return SLOCPerCC the SLOc per CC of the folder
	 */
	public float getSLOCPerCC() {

		float SLOCPerCC = getTotalSLOC() / getTotalCC();

		return SLOCPerCC;
	}

	/**
	 * Gets the total CR, recursively looks at all subfolder and files.
	 * 
	 * @return totalCR the total CR of the folder
	 */
	private float getAllFilesCR() {

		float totalCR = 0;

		// Recursively get the subfolder's total CR
		for (SFolder folder : subFolders) {
			totalCR += folder.getAllFilesCR();
		}

		// Get the all the file's total CR
		for (SFile file : allFiles) {
			totalCR += file.getCommentRatio();
		}

		return totalCR;
	}

	/**
	 * Gets the average CC of the folder.
	 * 
	 * @return average CC the average CC of the folder
	 */
	public float getAverageCC() {
		float averageCC = getTotalCC() / getTotalFiles();
		return averageCC;
	}

	/**
	 * Gets the average CR of this folder based on the number of files.
	 * 
	 * @return averageCR the average CR of the folder
	 */
	public float getAverageCR() {

		float totalCR = getAllFilesCR();
		float totalFiles = getTotalFiles();
		float averageCR = totalCR / totalFiles;

		return averageCR;
	}

	/**
	 * Gets the average SLOC of this folder based on the number of files.
	 * 
	 * @return averageSLOC the average SLOC of the folder
	 */
	public float getAverageSLOC() {

		float averageSLOC = getTotalSLOC() / getTotalFiles();

		return averageSLOC;
	}

	/**
	 * Gets the average SLOC per CC of this folder
	 * 
	 * @return averageSLOCPerCC the average SLOC per CC of the folder
	 */
	public float getAverageSLOCPerCC() {

		float averageSLOCPerCC = getAverageSLOC() / getAverageCC();

		return averageSLOCPerCC;
	}

	/**
	 * Gets the highest CC, recursively looks at all subfolder and files.
	 * 
	 * @return highCC the highest CC of the folder
	 */
	public int getHighCC() {
		int highCC = 0;
		int currentCC = 0;

		// Recursively look through subfolder's for the highest CC
		for (SFolder folder : subFolders) {
			currentCC = folder.getHighCC();

			if (currentCC > highCC) {
				highCC = currentCC;
			}
		}

		// Look at all the files for a higher CC
		for (SFile file : allFiles) {
			if (file.getCyclo() > highCC) {
				highCC = file.getCyclo();
			}
		}

		return highCC;
	}

	/**
	 * Gets the highest CR, recursively looks at all subfolder and files.
	 * 
	 * @return highCR the highest CR of the folder
	 */
	public float getHighCR() {

		float highCR = 0;
		float currentCR = 0;

		// Recursively look through subfolder's for the highest CR
		for (SFolder folder : subFolders) {
			currentCR = folder.getHighCR();

			if (currentCR > highCR) {
				highCR = currentCR;
			}
		}

		// Look at all the files for a higher CR
		for (SFile file : allFiles) {
			if (file.getCommentRatio() > highCR) {
				highCR = file.getCommentRatio();
			}
		}

		return highCR;
	}

	/**
	 * Gets the highest SLOC, recursively looks at all subfolder and files.
	 * 
	 * @return highSLOC the highest SLOC of the folder
	 */
	public float getHighSLOC() {
		float highSLOC = 0;
		float currentSLOC = 0;

		// Recursively look through subfolder's for the highest SLOC
		for (SFolder folder : subFolders) {
			currentSLOC = folder.getHighSLOC();

			if (currentSLOC > highSLOC) {
				highSLOC = currentSLOC;
			}
		}

		// Look at all the files for a higher SLOC
		for (SFile file : allFiles) {
			if (file.getSourceLines() > highSLOC) {
				highSLOC = file.getSourceLines();
			}
		}

		return highSLOC;
	}

	/**
	 * Gets the lowest CC, recursively looks at all subfolder and files.
	 * 
	 * @return lowCC the lowest CC of the folder
	 */
	public int getLowCC() {

		int lowCC = 65534;
		int currentCC = 65534;

		// Recursively look through subfolder's for the lowest CC
		for (SFolder folder : subFolders) {
			currentCC = folder.getLowCC();

			if (currentCC < lowCC) {
				lowCC = currentCC;
			}
		}

		// Look at all the files for a lower CC
		for (SFile file : allFiles) {
			if (file.getCyclo() < lowCC) {
				lowCC = file.getCyclo();
			}
		}

		return lowCC;
	}

	/**
	 * Gets the lowest CR, recursively looks at all subfolder and files.
	 * 
	 * @return lowCR the lowest CR of the folder
	 */
	public float getLowCR() {

		float lowCR = 65534;
		float currentCR = 65534;

		// Recursively look through subfolder's for the lowest CR
		for (SFolder folder : subFolders) {
			currentCR = folder.getLowCR();

			if (currentCR < lowCR) {
				lowCR = currentCR;
			}
		}

		// Look at all the files for a lower CR
		for (SFile file : allFiles) {
			if (file.getCommentRatio() < lowCR) {
				lowCR = file.getCommentRatio();
			}
		}

		return lowCR;
	}

	/**
	 * Gets the lowest SLOC, recursively looks at all subfolder and files.
	 * 
	 * @return lowSLOC the lowest SLOC of the folder
	 */
	public float getLowSLOC() {
		float lowSLOC = 65534;
		float currentSLOC = 65534;

		// Recursively look through subfolder's for the lowest SLOC
		for (SFolder folder : subFolders) {
			currentSLOC = folder.getLowSLOC();

			if (currentSLOC < lowSLOC) {
				lowSLOC = currentSLOC;
			}
		}

		// Look at all the files for a lower SLOC
		for (SFile file : allFiles) {
			if (file.getSourceLines() < lowSLOC) {
				lowSLOC = file.getSourceLines();
			}
		}

		return lowSLOC;
	}
}